TITLE PDP-8 Emulator                       (main.asm)

;;//Description: FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFfffffffffffffffffffffffffffffffffffffffffffffffffffffffffyessssssssssssssssss
;;//
;;//Revision date:

INCLUDE Irvine32.inc
.data
;;//instruction & instructionNames store the binary values and human-readable representations of the 8 possible PDP8 instructions.
instructions        WORD 000b, 001b, 010b, 011b, 100b, 101b, 110b, 111b
instructionNames    BYTE "AND", 0, "TAD", 0, "ISZ", 0, "DCA", 0, "JMS", 0, "JMP", 0, "IOT", 0, "OPR", 0

;;//----------------------------------------------------------------------------
;;//After parsing a PDP8 word, the following variables store the pertinant data:

;;//stores a 3 bit value corresponding to the instruction to be preformed
instruction         BYTE ?

;;//A single bit that flags the indirect bit as on or off
indirectBit         BYTE ?

;;//A single bit that flags the page bit as on or off
pageBit             BYTE ?

;;//stores the address/constant value from the instruction
address             BYTE ?

;;//stores the FINAL address (after possible correction for the page bit/indirect bit) of the address/constant from the instruction
absoluteAddress     WORD ?

;;//stores the FINAL *VALUE* at the absouteAddress
absoluteValue       WORD ?
;;//----------------------------------------------------------------------------

sampleInstructions  WORD 0000000010000001b,0000110101101011b,0000111001010110b,0000000101101111b

;;//The following variables are core components of the PDP8 emulator:

;;//A PDP8 word that represents the current instruction
programCounter      WORD 0

;;//The PDP8's single register
accumulator         WORD 0

;;//Represents the current page of memory we're dealing with.
currentPage         BYTE 0

;;//The PDP8 has a memory of 4k words
memory              WORD 4096 DUP(0)

.code
;;//This function steps the program counter, accounting for page number
StepProgramCounter PROC USES EAX EDX
     INC programCounter

     RET
StepProgramCounter ENDP

;;//INSTRUCTION 000
;;//This instruction ANDs the value in the instruction with the accumulator
InstructionAND PROC USES EAX
     ;;//First thing's first, let's clear EAX and move the absolute address into it
     MOV EAX, 0
     MOV AX, absoluteValue

     AND accumulator, AX

     ;;//Step the program counter
     CALL StepProgramCounter
     RET
InstructionAND ENDP

;;//INSTRUCTION 001
;;//THis instruction adds the memory operand to the accumulator
InstructionTAD PROC USES EAX
     ;;//First thing's first, let's clear EAX and move the absolute address into it
     MOV EAX, 0
     MOV AX, absoluteValue

     ADD accumulator, AX

     ;;//Step the program counter
     CALL StepProgramCounter
     RET
InstructionTAD ENDP

;;//INSTRUCTION 002
;;//This instruction imcrements the memory operand and skips the next instruction if the result was zero
InstructionISZ PROC USES EAX
     ;;//First thing's first, let's clear EAX and move the absolute address into it
     MOV EAX, 0
     MOV AX, absoluteAddress

     ;;//Now, let's increment the value in PDP8 memory there
     INC [memory+AX]

     ;;//And now we have to check to see if it's 0 or not
     MOV AX, [memory+AX]
     .IF AX == 0
          ;;//If it is, skip the next instruction
          INC programCounter
     .ENDIF

     ;;//Step the program counter
     CALL StepProgramCounter
     RET
InstructionISZ ENDP

;;//INSTRUCTION 003
;;//This instruction writes the accumulator to the memory address and then clears the accumulator
InstructionDCA PROC USES EAX EDX
     ;;//First thing's first, let's clear EAX and move the absolute address into it
     MOV EAX, 0
     MOV AX, absoluteAddress

     ;;//Now, let's write the accumulator to the address in the PDP8 memory pool
     ;;//Temporarily store the accumulator in DX
     MOV DX, accumulator
     ;;//Move DX (accumulator) into the proper PDP8 memory location
     MOV [memory+AX], DX
     
     ;;//Now, clear the accumulator
     MOV accumulator, 0

     ;;//Step the program counter
     CALL StepProgramCounter
     RET
InstructionDCA ENDP

;;//INSTRUCTION 004
;;//This instruction preforms a "Jump to Subroutine".
;;//It points the programCounter to the specified location +1, putting the return address (PC + 1) at the specified location.
InstructionJMS PROC USES EAX EDX
     ;;//First thing's first, let's clear EAX and move the absolute address into it
     MOV EAX, 0
     MOV AX, absoluteAddress

     ;;//Now, we have to put the return address at the very beginning of the subroutine
     ;;//Temporarily remember the program counter in DX
     MOV DX, programCounter
     ;;//Increment DX (so that when we return, we return after the JMS statement, not into it)
     INC DX
     ;;//Now, put our updated return address into PDP8 memory at the top of the subroutine
     MOV [memory+AX], DX

     ;;//Now, we need to move the program counter to the specified location + 1 (the start of the subroutine
     ;;//Increment AX to get the location of the first line of the subroutine
     INC AX
     ;;//Move the program counter to AX
     MOV programCounter, AX

     RET
InstructionJMS ENDP

;;//INSTRUCTION 005
;;//Execute a Jump- move the program counter to the specified absolute address.
InstructionJMP PROC USES EAX
     ;;//First thing's first, let's clear EAX and move the absolute address into it
     MOV EAX, 0
     MOV AX, absoluteAddress

     ;;//Now, let's update the program counter with the absolute address
     MOV programCounter, AX

     RET
InstructionJMP ENDP

;;//Get absolute address (modified by page bit)
GetAbsoluteAddress PROC USES EAX EDX
    
     ;;//Clear out EAX
     MOV EAX, 0

     ;;//PART 1:

     ;;//If the pagebit is on, that means that the address in the instruction referenced the current page
     ;;//so we must find the absolute address of the instruction relative to the current page.
     .IF pageBit == 1
          ;;//We need to find the absolute starting address of the current page, so lets multiply the current page by 128 (page size)
          ;;//We do this by putting the current page in AX, then shifting it right 7 times (multiplying it by 128)
          MOVZX AX, currentPage
          SHL AX, 7

          ;;//Now we have the absolute address of the beginning of the current page in memory. All we need to do is add the offset specified
          ;;//by the address specified in the current instruction.
     .ENDIF
     
     ;;//(temporarily use EDX to store the raw address as a 16 bit value)
     MOVZX DX, address

     ;;//Add the address we got from the instruction to AX, then put AX into absoluteAddress
     ;;//And we can then clear DX
     ADD AX, DX
     MOV DX, 0

     MOV absoluteAddress, AX

     ;;//PART 2:
     ;;//If the indirect bit is on, it means that the value at the address we got earlier is actually a pointer to the value at another address. Ugh.
     ;;//We resolve this by setting absolute address to the value at the base location of memory offset by whatever is currently in AX
     .IF indirectBit == 1
          ;;//Temporarily remember the value of the target address in DX
          MOV DX, [memory+AX]
          
          ;;//Put that value in absoluteAddress
          MOV absoluteAddress, DX
     .ENDIF

     ;;//Now we have an address in absoluteAddress, so let's get the value at that address and put it in absoluteValue
     MOV AX, absoluteAddress
     MOV DX, [memory+AX]
     MOV absoluteValue, DX

     RET
GetAbsoluteAddress ENDP

;;//Parses the address
GetAddress PROC
    ;;//backup the instruction in DX
    MOV DX, AX

    ;;//AND out the address portion of the instruction
    AND AX, 0000000001111111b
   
    ;;//store the address
    MOV address, AL
   
    ;;//restore AX
    MOV AX, DX
   
    RET
GetAddress ENDP

;;//Parses the page bit
GetPageBit PROC USES EDX
    ;backup the instruction in DX
    MOV DX, AX

    AND AX, 0000000010000000b
   
    ;;//AND out the page bit
    .IF AX == 0000000010000000b
	   MOV pageBit, 1
    .ELSE
	   MOV pageBit, 0
    .ENDIF
   
    ;;//restore AX
    MOV AX, DX
   
    RET
GetPageBit ENDP

;;//Parses the indirect bit
GetIndirectBit PROC USES EDX
    ;backup the instruction in DX
    MOV DX, AX

    ;;//AND out the indirect bit
    AND AX, 0000000100000000b
   
    .IF AX == 0000000100000000b
	   MOV indirectBit, 1
    .ELSE
	   MOV indirectBit, 0
    .ENDIF
   
    ;;//restore AX
    MOV AX, DX
   
    RET
GetIndirectBit ENDP

;;//Parses the instruction in AX
GetInstruction PROC USES EDX
    ;;//backup the instruction in DX
    MOV DX, AX
   
    ;;//shift right to put the instruction in the bottom 3 bits
    SHR AX, 9
   
    ;;//Move the AL into instruction
    MOV instruction, AL
   
    ;;//Restore AX
    MOV AX, DX
   
    RET
GetInstruction ENDP

;;//Prints the mnemonic for the instruction in instruction
PrintInstructionName PROC USES ECX EDX
    ;;//clear out ECX, then fill the lower 8 bits with the instruction
    MOV ECX, 0
    MOV CL, instruction

    ;;//multiply the instruction's numerical value by 4
    SHL ECX, 2

    ;;//move the beginning of the instruction mnemonics into EDX
    MOV EDX, OFFSET [instructionNames]
   
    ;;//move EDX to the name of the proper instruction
    ADD EDX, ECX

    CALL WriteString

    RET
PrintInstructionName ENDP

;;//Writes a memory address to the console in octal
OutputPDP8AddressAsOctal PROC USES EAX
    ;;//Write the first digit of the address
    MOV EAX, 0
    MOV AL, address
    AND AL, 11000000b
    SHR AL, 6
    CALL WriteDec
   
    ;;//Write the second digit of the address
    MOV EAX, 0
    MOV AL, address
    AND AL, 00111000b
    SHR AL, 3
    CALL WriteDec
   
    ;;//Write the second digit of the address
    MOV EAX, 0
    MOV AL, address
    AND AL, 00000111b
    CALL WriteDec
   
    RET
OutputPDP8AddressAsOctal ENDP

main PROC
    
     ;;//Okay, let's initalize our program counter and current page tracker
     MOV programCounter, 0
     MOV currentPage, 0

     ;;//This is where we'd proabably stick some code in PDP8 memory to do stuff with...

;;//Here, we begin parsing a PDP8 instruction in memory
beginParseInstruction:

     ;;//First things first- move the current instruction into EAX so it can be parsed
     ;;//To do this, we have to get the current page:
     MOV AX, programCounter
     ;;//shift the bottom 7 bits of the PC off the end, leaving us with the current page num
     SHR AX, 7
     ;;//Move the lower half of AX into currentPage
     MOV currentPage, AL
     ;;//Now we have the current page- let's put programCounter back in AX, and the bottom 7 bits will be our memory location
     MOV EAX, 0
     MOV AX, programCounter
     ;;//Mask off our bottom 7 bits
     AND AX, 0000000001111111b
     ;;//Now we'll use DX to temporarily calculate the address of the current instruction
     MOV EDX, 0
     MOVZX DX, currentPage
     ;;//Multiply the current page by 128 and add whatever was left in the programCounter
     SHL DX, 7
     ADD DX, AX
     ;;//Now we just need to put the current PDP8 memory address in EAX so we can parse it
     MOV AX, [memory+DX]
          
     ;;//Parse all the information out of the instruction
     CALL GetInstruction
     CALL GetIndirectBit
     CALL GetPageBit
     CALL GetAddress
     CALL GetAbsoluteAddress

     ;;//Now, based on the instruction, preform the proper action
     .IF instruction == 0
          CALL InstructionAND
     .ENDIF
     .IF instruction == 1
          CALL InstructionTAD
     .ENDIF
     .IF instruction == 2
          CALL InstructionISZ
     .ENDIF
     .IF instruction == 3
          CALL InstructionDCA
     .ENDIF
     .IF instruction == 4
          CALL InstructionJMS
     .ENDIF
     .IF instruction == 5
          CALL InstructionJMP
     .ENDIF

     ;;//And we're ready to parse the next instruction!
     JMP beginParseInstruction

     exit
main ENDP

END main
